﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Tomahawk.Runtime.Canvas.Sprite;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Xml.Serialization;

namespace Tomahawk.Runtime.LoadingScreens
{
    public class UICanvas
    {
        #region SpriteRenderBatch
        [XmlType("UICanvas.SpriteRenderBatch")]
        public class SpriteRenderBatch
        {
            public string spriteEffect = "Default";
            public List<UIControl> sprites = new List<UIControl>();
        }
        #endregion

        #region Properties
        public Vector2 virtualCanvasScale = Vector2.One;
        public Color overlayColor = Color.White;

        private Dictionary<string, SpriteEffect> spriteEffects = new Dictionary<string, SpriteEffect>();
        private List<UIControl> sprites = new List<UIControl>();
        private List<SpriteRenderBatch> spriteBatches = new List<SpriteRenderBatch>();
        private SpriteBatch spriteBatch = null;
        #endregion

        #region Sprite Management
        public void AddSprite(UIControl control)
        {
            sprites.Add(control);
        }

        public void RemoveSprite(UIControl control)
        {
            sprites.Remove(control);
        }
        #endregion

        #region Effect Management
        internal bool RegisterEffectToChannel(SpriteEffect effect, string channel)
        {
            SpriteEffect currentEffect;
            if (!spriteEffects.TryGetValue(channel, out currentEffect))
            {
                spriteEffects.Add(channel, effect);
                return true;
            }
            return false;
        }

        internal bool UnregisterEffectFromChannel(SpriteEffect effect, string channel)
        {
            SpriteEffect currentEffect;
            if (spriteEffects.TryGetValue(channel, out currentEffect))
            {
                if (currentEffect == effect)
                {
                    spriteEffects.Remove(channel);
                    return true;
                }
            }
            return false;
        }

        internal SpriteEffect GetEffectAssignedToChannel(string channel)
        {
            SpriteEffect currentEffect = null;
            spriteEffects.TryGetValue(channel, out currentEffect);
            return currentEffect;
        }
        #endregion

        #region Virtual canvas

        public void ApplyVirtualCanvas(ref Microsoft.Xna.Framework.Rectangle canvasRectangle, ref Microsoft.Xna.Framework.Rectangle screenRectangle)
        {
            screenRectangle.X = (int)(virtualCanvasScale.X * canvasRectangle.X);
            screenRectangle.Y = (int)(virtualCanvasScale.Y * canvasRectangle.Y);
            screenRectangle.Width = (int)(virtualCanvasScale.X * canvasRectangle.Width);
            screenRectangle.Height = (int)(virtualCanvasScale.Y * canvasRectangle.Height);
        }

        public void ApplyVirtualCanvas(ref Microsoft.Xna.Framework.Vector2 canvasPosition, ref Microsoft.Xna.Framework.Vector2 screenPosition)
        {
            screenPosition.X = virtualCanvasScale.X * canvasPosition.X;
            screenPosition.Y = virtualCanvasScale.Y * canvasPosition.Y;
        }

        public void ApplyVirtualCanvas(float canvasSize, out float screenSize)
        {
            screenSize = 0.5f * (virtualCanvasScale.X + virtualCanvasScale.Y) * canvasSize;
        }

        #endregion

        #region Initialization
        public UICanvas(GraphicsDevice graphicsDevice)
        {
            spriteBatch = new SpriteBatch(graphicsDevice);
        }
        #endregion

        #region Render
        private int CompareSpritesByDepth(UIControl a, UIControl b)
        {
            int diff = (int)(1000.0f * (b.Depth - a.Depth));
            return (diff != 0) ? diff : string.Compare(b.EffectChannel, a.EffectChannel);
        }

        protected void RebuildRenderBatches()
        {
            spriteBatches.Clear();
            sprites.Sort(CompareSpritesByDepth);

            SpriteRenderBatch currentRenderBatch = null;
            foreach (UIControl sprite in sprites)
            {
                if (currentRenderBatch == null || (currentRenderBatch.spriteEffect != sprite.EffectChannel))
                {
                    currentRenderBatch = new SpriteRenderBatch();
                    currentRenderBatch.spriteEffect = sprite.EffectChannel;
                    spriteBatches.Add(currentRenderBatch);
                }
                currentRenderBatch.sprites.Add(sprite);
            }
        }

        internal void Render(bool rebuildBatches)
        {
            if (rebuildBatches)
            {
                RebuildRenderBatches();
            }

            foreach (SpriteRenderBatch batch in spriteBatches)
            {
                //Find the effect to use
                SpriteEffect effect = GetEffectAssignedToChannel(batch.spriteEffect);

                //Prepare the sprite batch
                spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.None);

                //Prepare the sprite effect
                if (effect != null)
                {
                    effect.Begin();
                }

                //render all uisprites
                foreach (UIControl sprite in batch.sprites)
                {
                    if (sprite.Visible)
                    {
                        sprite.Render(this, spriteBatch);
                    }
                }

                //end the spritebatch
                spriteBatch.End();

                //end the sprite effect
                if (effect != null)
                {
                    effect.End();
                }
            }
        }
        #endregion
    }
}
